package com.zyl.blog.gateway.filter;

import cn.dev33.satoken.reactor.filter.SaReactorFilter;
import cn.dev33.satoken.router.SaRouter;
import cn.dev33.satoken.stp.StpUtil;
import cn.dev33.satoken.util.SaResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.validator.routines.InetAddressValidator;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.stream.Collectors;

/**
 * @version v1.0
 * @ProjectName: Personal_Blog
 * @Package: com.zyl.gateway.filter
 * @ClassName: GatewayConfig
 * @Description: 网关鉴权配置
 * @Author FinalFantasy
 * @Date 2025/6/24-15:02
 */
@Configuration
@Slf4j
public class GatewayConfig {

    // 统一鉴权过滤器
    @Bean
    public SaReactorFilter getSaReactorFilter() {
        return new SaReactorFilter()
                .addInclude("/**")
                // 直接放行以下路径（不进入鉴权逻辑）
                .addExclude(
                        "/favicon.ico",
                        "/rbac/verify/**",
                        "/rbac/user/login",
                        "/rbac/user/register",
                        "/public/**",
                        "/ws/monitoring",  // 精确匹配
                        "/ws/**"           // 通配所有WebSocket端点
                )
                .setAuth(obj -> {
                    SaRouter.match("/**",
                            r -> {
                                log.debug("执行登录校验，当前token：{}", StpUtil.getTokenValue());
                                StpUtil.checkLogin();
                            });
                })
                // 异常处理方法
                .setError(e -> {
                    log.error("网关鉴权异常: {}", e.getMessage());
                    return SaResult.error(e.getMessage());
                });
    }

    // 全局日志过滤器
    @Bean
    public GlobalFilter customGlobalFilter() {
        return (exchange, chain) -> {
            long startTime = System.currentTimeMillis();
            ServerWebExchange mutatedExchange = exchange;

            if (log.isInfoEnabled()) {
                // 结构化日志输出
                String path = exchange.getRequest().getPath().toString();
                String method = exchange.getRequest().getMethod().toString();
                String headers = exchange.getRequest().getHeaders().entrySet().stream()
                        .map(entry -> String.format("    %-20s = %s", entry.getKey(), entry.getValue()))
                        .collect(Collectors.joining("\n"));

                String template = """
                        \n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
                        [Gateway Request]
                        | Path:    {}
                        | Method:  {}
                        | Headers:
                        {}
                        <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                        """;

                log.info(template, path, method, headers);

                mutatedExchange = exchange.mutate().response(new LoggingResponseDecorator(
                        exchange.getResponse(),
                        () -> {
                            long duration = System.currentTimeMillis() - startTime;
                            int status = exchange.getResponse().getStatusCode() != null ?
                                    exchange.getResponse().getStatusCode().value() : 500;
                            String template2 = """
                                    \n========================
                                    [Gateway Response]
                                    | Status:  {}
                                    | Time:    {}ms
                                    ================================
                                    """;
                            log.info(template2, status, duration);
                        })).build();
            }

            return chain.filter(mutatedExchange);
        };
    }

    @Bean
    public GlobalFilter importantParamFilter() {
        return (exchange, chain) -> {
            ServerHttpRequest request = exchange.getRequest();

            // 1. 客户端IP解析（支持代理场景）
            String clientIp = resolveClientIp(request);
            String template = """
                    \n======================
                    [Client Info]
                    | IP:      {}
                    | Protocol: {}
                    ================================
                    """;
            log.debug(template, clientIp, request.getURI().getScheme());
            template = """
                    \n======================
                    [Request Headers]
                    {}
                    """;
            // 2. 关键请求头记录
            log.debug(template, formatHeaders(request));

            return chain.filter(exchange);
        };
    }

    // 判断是否为WebSocket握手请求
    private boolean isWebSocketUpgradeRequest(ServerHttpRequest request) {
        String upgradeHeader = request.getHeaders().getFirst("Upgrade");
        return "websocket".equalsIgnoreCase(upgradeHeader);
    }

    private String resolveClientIp(ServerHttpRequest request) {
        try {
            // 优先级：X-Forwarded-For > Proxy-Client-IP > WL-Proxy-Client-IP > RemoteAddr
            String[] headerNames = {
                    "X-Forwarded-For",
                    "Proxy-Client-IP",
                    "WL-Proxy-Client-IP"
            };

            for (String header : headerNames) {
                String ipList = request.getHeaders().getFirst(header);
                if (StringUtils.hasText(ipList) && !"unknown".equalsIgnoreCase(ipList)) {
                    String[] ips = ipList.split(",");
                    for (String ip : ips) {
                        String cleanIp = ip.trim();
                        if (isValidIp(cleanIp)) {
                            return normalizeIp(cleanIp);
                        }
                    }
                }
            }

            // 从远程地址获取
            if (request.getRemoteAddress() != null &&
                    request.getRemoteAddress().getAddress() != null) {
                String rawIp = request.getRemoteAddress().getAddress().getHostAddress();
                return normalizeIp(rawIp);
            }

            return "unknown";
        } catch (Exception e) {
            log.warn("IP解析失败: {}", e.getMessage(), e);
            return "parse_error";
        }
    }

    private String normalizeIp(String rawIp) {
        // IPv6本地地址转IPv4
        if ("0:0:0:0:0:0:0:1".equals(rawIp) || "::1".equals(rawIp)) {
            return "127.0.0.1";
        }
        // 简化IPv6显示
        if (rawIp.contains(":")) {
            return rawIp.replace("::", "_").replaceAll("(:0)+", ":");
        }
        return rawIp;
    }

    private boolean isValidIp(String ip) {
        return InetAddressValidator.getInstance().isValid(ip);
    }

    private String formatHeaders(ServerHttpRequest request) {
        return request.getHeaders().entrySet().stream()
                .map(entry -> String.format("| %-20s = %s",
                        entry.getKey(),
                        String.join(", ", entry.getValue())))
                .collect(Collectors.joining("\n"));
    }

}

// 响应装饰器用于记录响应日志
class LoggingResponseDecorator extends ServerHttpResponseDecorator {
    private final Runnable logCallback;

    public LoggingResponseDecorator(ServerHttpResponse delegate, Runnable logCallback) {
        super(delegate);
        this.logCallback = logCallback;
    }

    @Override
    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
        return super.writeWith(body).doOnSuccess(v -> logCallback.run());
    }
}
